/**
 * 
 */
package qy.jalgotrade.event;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import org.apache.commons.lang3.tuple.Pair;

/**
 * <pre>
 * EventName                        Params
 * broker-order-event               broker, orderEvent
 * dataseries-new-value-event       dataSeries, dateTime, value
 * feed-new-values-event            dateTime, values
 * rtnstratana-new-return-event     dateTime, returnsAnalyzerBase
 * strat-bar-proced-event           strategy, bars
 * dispatcher-start-event           ...
 * dispatcher-idle-event            ...
 * </pre>
 * 
 * @author c-geo
 *
 */
public class Event {

	private String __name;

	/**
	 * 观察者名单
	 */
	private List<EventHandler> __handlers;

	private List<Pair<Consumer<EventHandler>, EventHandler>> __deferred;

	private int __emitting; // TODO: 线程安全: self.__emitting

	public Event() {

		this("unknown-event");
	}

	public Event(String name) {

		__name = name;
		__handlers = new ArrayList<>();
		__deferred = new ArrayList<>();
		__emitting = 0;
	}

	/**
	 * @return the name
	 */
	public String getName() {

		return __name;
	}

	private void __subscribeImpl(EventHandler handler) {

		assert __emitting == 0;
		if (!__handlers.contains(handler)) {
			__handlers.add(handler);
		}
	}

	private void __unsubscribeImpl(EventHandler handler) {

		assert __emitting == 0;
		__handlers.remove(handler);
	}

	private void __applyChanges() {

		assert __emitting == 0;
		for (Pair<Consumer<EventHandler>, EventHandler> e : __deferred) {
			e.getLeft().accept(e.getRight());
		}
	}

	/**
	 * 添加 订阅 (即对此类型 事件 进行 响应 的 观察者):
	 * 
	 * @param handler
	 */
	public void subscribe(EventHandler handler) {

		if (__emitting > 0) {
			__deferred.add(Pair.of(this::__subscribeImpl, handler));
		} else if (!__handlers.contains(handler)) {
			__subscribeImpl(handler);
		}
	}

	/**
	 * 取消订阅:
	 * 
	 * @param handler
	 */
	public void unsubscribe(EventHandler handler) {

		if (__emitting > 0) {
			__deferred.add(Pair.of(this::__unsubscribeImpl, handler));
		} else {
			__unsubscribeImpl(handler);
		}
	}

	/**
	 * 
	 * @param eventName
	 * @param params
	 */
	public void emit(String eventName, Map<String, Object> params) {

		try {
			__emitting += 1;
			for (EventHandler e : __handlers) {
				e.handle(eventName, params);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			__emitting -= 1;
			if (__emitting == 0) {
				__applyChanges();
			}
		}
	}
}
